﻿//AcademicPlanner - course registration planning web app.
//Copyright (C) 2009  Boubacar Diallo

//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AcademicPlanner.Data;
using AcademicPlanner.Data.Entities;
using AcademicPlanner.Data.Repositories;
using AcademicPlanner.Domain.Models.Collections;
using AcademicPlanner.Framework.Helpers;
using AcademicPlanner.Framework.Libraries.Validation;
using AcademicPlanner.Domain.Components;

namespace AcademicPlanner.Domain.Models
{
	/// <summary>
	/// Registration represents an academic registration to an academic course in the application.
	/// </summary>
	/// <since>1.0</since>
	public class Registration : Model
	{
		/// <summary>
		/// The minimum value of the <see cref="P:Year"/> property of a Registration.
		/// </summary>
		/// <since>1.0</since>
		public static readonly double YEAR_MIN_VALUE = 0;

		/// <summary>
		/// The <c>Course</c> of this Registration.
		/// </summary>
		/// <since>1.0</since>
		protected Course course;

		/// <summary>
		/// The <c>AcademicSchedule</c> this Registration belongs to.
		/// </summary>
		/// <since>1.0</since>
		protected AcademicSchedule schedule;

		/// <summary>
		/// The <c>AcademicTerm</c> this Registration happens in.
		/// </summary>
		/// <since>1.0</since>
		protected AcademicTerm term;


		/// <summary>
		/// Gets the data layer repository for this Registration's underlying data representation.
		/// </summary>
		/// <since>1.0</since>
		protected override EntityRepository EntityRepo { get { return DomainMaster.DSAS.RegistrationEntityRepo; } }

		/// <summary>
		/// Gets this Registration's underlying data representation.
		/// </summary>
		/// <since>1.0</since>
		protected RegistrationEntity Entity { get { return (RegistrationEntity)entity; } }

		/// <summary>
		/// Gets the year this Registration happens in.
		/// </summary>
		/// <since>1.0</since>
		public int Year
		{
			get
			{
				return Entity.PeriodEntity.Year;
			}
		}

		/// <summary>
		/// Gets the <c>AcademicSchedule</c> this Registration belongs to.
		/// </summary>
		/// <since>1.0</since>
		public AcademicSchedule Schedule
		{
			get
			{
				if (schedule == null && Entity.ScheduleEntity != null)
					schedule = DomainMaster.ModelsFountain.GetAcademicSchedule(Entity.ScheduleEntity);
				return schedule;
			}
		}

		/// <summary>
		/// Gets the <c>Course</c> of this Registration.
		/// </summary>
		/// <since>1.0</since>
		public Course Course
		{
			get
			{
				if (course == null && Entity.CourseEntity != null)
					course = DomainMaster.ModelsFountain.GetCourse(Entity.CourseEntity);
				return course;
			}
		}

		/// <summary>
		/// Gets the <c>AcademicTerm</c> this Registration happens in.
		/// </summary>
		/// <since>1.0</since>
		public AcademicTerm Term
		{
			get
			{
				SetTerm();
				return term;
			}
		}


		/// <summary>
		/// Constructs a new Registration.
		/// </summary>
		/// <remarks>
		/// This constructor invokes the matching super constructor.
		/// </remarks>
		/// <since>1.0</since>
		public Registration()
			: base()
		{
			
		}

		/// <summary>
		/// Constructs a new Registration with the provided <c>RegistrationEntity</c>.
		/// </summary>
		/// <remarks>
		/// This constructor invokes the matching super constructor.
		/// </remarks>
		/// <param name="pEntity">The Registration's <c>RegistrationEntity</c>.</param>
		/// <since>1.0</since>
		public Registration(RegistrationEntity pEntity) 
			: base(pEntity)
		{
			
		}


		private void SetTerm()
		{
			if (term == null)
				ResetStartTerm();
		}

		private void ResetStartTerm()
		{
			term = DomainMaster.ModelsFountain.GetAcademicTerm(Entity.PeriodEntity.TermEntity);
		}

		/// <summary>
		/// Validates a possible value of the <see cref="Year"/> property
		/// of a Registration.
		/// </summary>
		/// <remarks>
		/// The value must the bigger than <see cref="P:YEAR_MIN_VALUE"/>.
		/// </remarks>
		/// <param name="pVal">The possible value to validate.</param>
		/// <returns>A validation error code.</returns>
		public static InvalidModelPropertyErrorCode ValidateYear(int pVal)
		{
			var rVal = InvalidModelPropertyErrorCode.NONE;

			if (pVal <= YEAR_MIN_VALUE)
				rVal = InvalidModelPropertyErrorCode.TOO_SMALL;

			return rVal;
		}

		internal void Set(int pYear, AcademicTerm pTerm, Course pCourse)
		{
			var vErrs = Validate(pYear, pTerm, pCourse);

			if (vErrs.OuterErrors.Count > 0)
				throw new InvalidModelPropertiesException(DomainErrorCode.M_INVALID_REGISTRATION_PROPERTIES, vErrs);

			if (Schedule != null)
			{
				vErrs = Schedule.ValidateRegistration(entity.Id, pYear, pTerm, pCourse);
				if (vErrs.OuterErrors.Count > 0)
					throw new InvalidModelPropertiesException(DomainErrorCode.M_INVALID_REGISTRATION_PROPERTIES, vErrs);
			}

			Entity.PeriodEntity.Year = pYear;
			Entity.PeriodEntity.TermEntity = (AcademicTermEntity)GetOtherModelEntity(pTerm);
			Entity.CourseEntity = (CourseEntity)GetOtherModelEntity(pCourse);
		}

		/// <summary>
		/// Validates possible values of a Registration's properties.
		/// </summary>
		/// <param name="pYear">The Registration year.</param>
		/// <param name="pTerm">The Registration <c>AcademicTerm</c>.</param>
		/// <param name="pCourse">The <c>Course</c> of the Registration.</param>
		/// <returns>A Collection of validation errors.</returns>
		/// <seealso cref="M:ValidateYear"/>
		/// <since>1.0</since>
		public static InvalidModelPropertiesErrorCollection Validate(int pYear, AcademicTerm pTerm, Course pCourse)
		{
			var rVal = new InvalidModelPropertiesErrorCollection("Registration");
			InvalidModelPropertyErrorCode vCode;

			if (!(vCode = ValidateYear(pYear))
					.Equals(InvalidModelPropertyErrorCode.NONE))
				rVal.AddOuter("Year", vCode);

			if (pCourse == null)
				rVal.AddOuter("Course", InvalidModelPropertyErrorCode.REQUIRED);

			if (pTerm == null)
				rVal.AddOuter("Term", InvalidModelPropertyErrorCode.REQUIRED);

			return rVal;
		}
	}
}
